import os
import numpy as np
import threading
import signal
import subprocess
from queue import deque
import time
from typing import Tuple

class AudioCaptureDevice():
    def __init__(self,
                 device_name: str,
                 output_format: str,
                 channels: int,
                 sample_rate: int,
                 buffer_duration: float
                 ):
        self.commands = [
            'arecord',
            '-D', device_name,
            '-f', output_format,
            '-c', str(channels),
            '-r', str(sample_rate),
            '-t', 'raw'
            ]
        self.channels = channels
        self.sample_rate = sample_rate
        self.process = None
        self.thread = threading.Thread()
        self.chunk_size = int(sample_rate/10*2*channels)
        self.buffer_size = int(buffer_duration * sample_rate / (self.chunk_size // channels))
        self.is_running = False
        self.read_write_lock = threading.Lock()
        self.audio_buffer = deque(maxlen=self.buffer_size)
    def capture_process(self):
        print('Audio capture process started with command: "%s"'%(' '.join(self.commands)))
        self.process = subprocess.Popen(self.commands,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
        while self.is_running:
            chunk = self.process.stdout.read(self.chunk_size)
            if not chunk:
                break
            with self.read_write_lock:
                self.audio_buffer.append(chunk)
        self.process.stdout.close()
        self.process.wait()
        print('Audio capture process closed.')
    def start_capture(self):
        self.is_running = True
        self.thread.daemon = True
        self.thread.run = self.capture_process
        self.thread.start()
    def stop_capture(self):
        self.is_running = False
        if self.process:
            self.process.terminate()
            time.sleep(0.02)
            if self.process.poll() is None:
                os.kill(self.process.pid,signal.SIGKILL)
            self.process.wait()
        self.thread.join()

    def get_audio_segment(self,duration,channel_indexes: Tuple = (0,)):
        samples = int(self.sample_rate * duration)
        while True:
            with self.read_write_lock:
                if len(self.audio_buffer) * self.chunk_size // (self.channels * 2) >= samples:
                    audio_data = b''.join(list(self.audio_buffer)[-samples // (self.chunk_size // (self.channels*2)):])
                    break
            time.sleep(0.2)
        channel_data_list = []
        for i in range(len(channel_indexes)):
            channel_data_list.append(np.frombuffer(audio_data,dtype=np.int16).reshape(-1,self.channels)[:,channel_indexes[i]])
        return channel_data_list
